﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Deployment.Application;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Shell;
using ViewMaker;
using ViewMaker.Core;
using ViewMaker.Core.Wpf;

namespace CloudStorageLight.WpfClient
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    [ExcludeFromCodeCoverage]
    public partial class MainWindow : Window
    {
        internal MainVM ViewModel { get; set; }

        private JumpList jumpList = new JumpList();

        public MainWindow()
        {
            InitializeComponent();
            ViewModel = new MainVM();
            this.DataContext = ViewModel;
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ViewModel.Init();
            ViewModel.ViewCommandNotified+=ViewModel_ViewCommandNotified;
            Files.Drop += Files_Drop;
            //Files.PreviewMouseLeftButtonDown += Files_MouseLeftButtonDown;
            Folders.Drop += Folders_Drop;

            Files.PreviewMouseDown += Files_PreviewMouseDown;
            Files.PreviewMouseMove += Files_PreviewMouseMove;
            Files.PreviewMouseUp += Files_PreviewMouseUp;
            FilesCont.PreviewKeyDown += Files_PreviewKeyDown;
            Files.SelectionChanged += (s, o) => { EndEditing(); };


            //Folders.PreviewKeyDown += Folders_PreviewKeyDown;

            Folders.AddHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(OnTreeItemExpanded));

            Folders.PreviewMouseDown += Folders_PreviewMouseDown;
            Folders.PreviewMouseUp += Files_PreviewMouseUp;
            Folders.PreviewMouseMove += Folders_PreviewMouseMove;
            ViewModel.PropertyChanged += ViewModel_PropertyChanged;
            UpdateJumpList();

            if (!string.IsNullOrEmpty(ViewModel.Settings.FileSortHeader))
            {
                Sort(ViewModel.Settings.FileSortHeader, ViewModel.Settings.FileSortAscending ? ListSortDirection.Ascending : ListSortDirection.Descending);
            }

        }

        void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "RecentFiles")
            {
                UpdateJumpList();
            }
        }

        private void UpdateJumpList()
        {
            jumpList.JumpItems.Clear();
            foreach (var item in ViewModel.RecentFiles)
            {
                jumpList.JumpItems.Add(new JumpTask()
                {
                    Title = item.Path.Split('/').LastOrDefault(),
                    Description = item.Path,
                    Arguments = "\"" + item.LocalPath + "\"",
                    WorkingDirectory = System.IO.Path.GetDirectoryName(item.LocalPath),
                    ApplicationPath = ViewModel.FileExtentionInfo(System.IO.Path.GetExtension(item.LocalPath)),
                    IconResourcePath = ViewModel.FileExtentionInfo(System.IO.Path.GetExtension(item.LocalPath)),
                });
            }
            jumpList.Apply();
        }

        void ViewModel_ViewCommandNotified(object sender, ViewMaker.Core.ViewCommandEventArgs e)
        {
            if (e.Command == ViewCommands.ShowMessage)
            {
                var info = (ShowMessageViewCommandInfo)e.Parameter;
                switch (info.Style)
                {
                    case MessageBoxStyle.Ok:
                        Dispatcher.Invoke(() => {
                            MessageBox.Show(this, info.Message, info.Title, MessageBoxButton.OK);
                        });
                        info.Result = true;
                        return;
                    case MessageBoxStyle.OkCancel:
                        Dispatcher.Invoke(() => {
                            info.Result = MessageBox.Show(this, info.Message, info.Title, MessageBoxButton.OKCancel) == MessageBoxResult.OK;
                        });
                        return;
                    default:
                        throw new NotImplementedException(info.Style.ToString());

                }
            }
        }

 


        private void Window_Unloaded(object sender, RoutedEventArgs e)
        {
            Folders.RemoveHandler(TreeViewItem.ExpandedEvent, new RoutedEventHandler(OnTreeItemExpanded)); 

        }

        #region Drag & Drop

        void Files_Drop(object sender, DragEventArgs e)
        {
            if (BlobDataManager.Instance.Offline) throw new ApplicationException(Properties.Resources.MsgCanNotWithOffline);
            if (_filesDragging) return; //自分のフォルダにドラッグはできない
            if (e.Data == null) return;

            IDataObject dataObj = e.Data as IDataObject;

            bool cut = false;
            MemoryStream stream = (MemoryStream)dataObj.GetData(CloudStorageLight.WpfClient.VirtualFileDataObject.NativeMethods.CFSTR_PREFERREDDROPEFFECT, true);
            if (stream != null)
            {
                int flag = stream.ReadByte();
                if (!(flag != 2 && flag != 5))
                {
                    cut = (flag == 2);
                }
            }
            string[] files = e.Data.GetData(DataFormats.FileDrop) as string[];
            var option = new Dictionary<string, object>();
            option.Add("Confirm", true);
            if (files != null)
            {
                ViewModel.UploadCommand.Execute(new FileCommandArg { Files = files, Data = option });
            }
            else if (dataObj.GetDataPresent(CloudStorageLight.WpfClient.VirtualFileDataObject.NativeMethods.CFSTR_FILEDESCRIPTORW))
            {
                var fileObj = new OutlookDataObject(dataObj);

                string[] filenames = (string[])fileObj.GetData(CloudStorageLight.WpfClient.VirtualFileDataObject.NativeMethods.CFSTR_FILEDESCRIPTORW);
                MemoryStream[] filestreams = (MemoryStream[])fileObj.GetData(CloudStorageLight.WpfClient.VirtualFileDataObject.NativeMethods.CFSTR_FILECONTENTS);

                var info = new FileCommandArg { Files = filenames, Data = option };
                var streams = new Dictionary<string, Stream>();
                info.Data["Stream"] = streams;
                for (int fileIndex = 0; fileIndex < filenames.Length; fileIndex++)
                {
                    string filename = filenames[fileIndex];
                    if (files != null && files.Contains(filename)) continue;

                    streams[filename] = filestreams[fileIndex];
                }
                ViewModel.UploadStreamCommand.Execute(info);

                while (ViewModel.IsBusy)
                {
                    ViewModel.DoEvents();
                }
                if (info.Data["Result"] != null) throw (Exception)info.Data["Result"];
            }
            else
            {
                throw new NotSupportedException();
            }
        }

        void Folders_Drop(object sender, DragEventArgs e)
        {
            if (BlobDataManager.Instance.Offline) throw new ApplicationException(Properties.Resources.MsgCanNotWithOffline);

            TreeViewItem treeItem = FindDropTarget(e);
            if (treeItem == null) return;
            var folder = treeItem.DataContext as DirectoryItem;
            if (folder != null)
            {
                if (e.Data == null) return;
                IDataObject dataObj = e.Data as IDataObject;
                string[] files = e.Data.GetData(DataFormats.FileDrop) as string[];
                if (files != null)
                {
                    var arg = new FileCommandArg { Path = folder.Path, Files = files };
                    ViewModel.UploadCommand.Execute(arg);

                }
                else if (e.Data.GetDataPresent(DataFormats.Serializable))
                {
                    files = ViewModel.GetDataObjectAsPaths(e.Data);
                    var arg = new FileCommandArg { Path = folder.Path, Files = files };

                    if (e.Effects == DragDropEffects.Copy)
                    {
                        ViewModel.CopyCommand.Execute(arg);

                    }
                    if (e.Effects == DragDropEffects.Move)
                    {
                        ViewModel.MoveCommand.Execute(arg);

                    }
                }
                else
                {
                    throw new NotSupportedException();
                }
                //string path = e.Data.GetData(DataFormats.StringFormat) as string;
                //if (path != null)
                //{
                //    var arg = new FileCommandArg { Path = folder.Path, Files = new string[] { path } };
                //    if (e.Effects == DragDropEffects.Copy)
                //    {
                //        ViewModel.CopyCommand.Execute(arg);

                //    }
                //    if (e.Effects == DragDropEffects.Move)
                //    {
                //        ViewModel.MoveCommand.Execute(arg);

                //    }
                //}
            }
        }

        private Point _filesOrigin;
        private bool _isFilesButtonDown;
        private bool _filesDragging;

        void Files_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            _isFilesButtonDown = false;
            _isFoldersButtonDown = false;
        }

        void Files_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton != MouseButtonState.Pressed || !_isFilesButtonDown)
            {
                return;
            }
            var fe = e.OriginalSource as FrameworkElement;
            if (fe == null || !(fe.DataContext is FileItem)) return;
            if (EditingFile != null) return; //ファイル名編集中はできない
            var point = e.GetPosition(null);
            if (CheckDistance(point, _filesOrigin))
            {
                if (_isFilesButtonDown)
                {
                    var dataObject = ViewModel.CreateFilesDataObject();
                    try
                    {
                        _filesDragging = true;
                        VirtualFileDataObject.DoDragDrop(sender as DependencyObject, dataObject, DragDropEffects.Copy);

                    }
                    finally
                    {
                        _filesDragging = false;
                        _isFilesButtonDown = false;
                    }

                }
                e.Handled = true;

            }
        }

        void Files_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (BlobDataManager.Instance.Offline) return;

            _filesOrigin = e.GetPosition(null);
            _isFilesButtonDown = true;

            Files.Focus();

            int index = this.GetCurrentIndex(e.GetPosition);

            if (index < 0 || !Files.SelectedItems.Cast<TreeItem>().Contains(Files.Items[index])) return;


        }

        delegate Point GetPositionDelegate(IInputElement element);

        int GetCurrentIndex(GetPositionDelegate getPosition)
        {
            int index = -1;
            for (int i = 0; i < this.Files.Items.Count; ++i)
            {
                ListViewItem item = GetListViewItem(i);
                if (this.IsMouseOverTarget(item, getPosition))
                {
                    index = i;
                    break;
                }
            }
            return index;
        }

        ListViewItem GetListViewItem(int index)
        {
            if (Files.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
                return null;

            return Files.ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem;
        }

        bool IsMouseOverTarget(Visual target, GetPositionDelegate getPosition)
        {
            if (target == null) return false;
            Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
            Point mousePos = getPosition((IInputElement)target);
            return bounds.Contains(mousePos);
        }


        private Point _foldersOrigin;
        private bool _isFoldersButtonDown;

        void Folders_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton != MouseButtonState.Pressed || !_isFoldersButtonDown)
            {
                return;
            }
            if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
            {
                var point = e.GetPosition(this);
                if (CheckDistance(point, _foldersOrigin))
                {
                    Debug.WriteLine("Mouse Move Handled");

                    if (_isFoldersButtonDown)
                    {
                        var item = Folders.SelectedItem as DirectoryItem;
                        if (item != null && item.LoadedChildren)
                        {
                            var dataObject = new VirtualFileDataObject();
                            ViewModel.SetPathToDataObject(dataObject, new string[] { item.Path }, false);
                            VirtualFileDataObject.DoDragDrop(sender as DependencyObject, dataObject, DragDropEffects.Copy);

                        }
                        _isFoldersButtonDown = false;
                    }

                    e.Handled = true;
                }
            }
        }

        private TreeViewItem GetTreeViewItem(Point p, TreeView treeView)
        {
            DependencyObject obj = treeView.InputHitTest(p) as DependencyObject;
            while (obj != null && !(obj is TreeViewItem))
                obj = VisualTreeHelper.GetParent(obj);
            return obj as TreeViewItem;
        }

        void Folders_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (BlobDataManager.Instance.Offline) return;

            _foldersOrigin = e.GetPosition(this);
            _isFoldersButtonDown = true;

        }



        private bool CheckDistance(Point x, Point y)
        {
            return Math.Abs(x.X - y.X) >= SystemParameters.MinimumHorizontalDragDistance * 4 ||
                Math.Abs(x.Y - y.Y) >= SystemParameters.MinimumVerticalDragDistance * 4;
        }

        private TreeViewItem FindDropTarget(DragEventArgs pDragEventArgs)
        {
            DependencyObject work = VisualTreeHelper.HitTest(Folders, pDragEventArgs.GetPosition(Folders)).VisualHit;

            while (work != null)
            {
                if (work is TreeViewItem)
                {
                    return work as TreeViewItem;

                }

                work = VisualTreeHelper.GetParent(work);
            }
            return null;
        }

        private void ListView_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            var item = ((FrameworkElement)e.OriginalSource).DataContext;
            if (EditingFile != null) return;
            ViewModel.ExecuteFileCommand.Execute(item);
        }

        #endregion

        #region listview manual

        GridViewColumnHeader _lastHeaderClicked = null;
        ListSortDirection _lastDirection = ListSortDirection.Ascending;

        private Dictionary<string, string> _headerColumn;

        private Dictionary<string, string> HeaderColumn
        {
            get 
            { 
                if (_headerColumn == null)
                {
                    _headerColumn = new Dictionary<string, string>();
                    _headerColumn.Add(Properties.Resources.MsgTitle, "Title");
                    _headerColumn.Add(Properties.Resources.MsgSize, "Length");
                    _headerColumn.Add(Properties.Resources.MsgLastModified, "LastModified");
                    _headerColumn.Add(Properties.Resources.MsgLastModifiedBy, "Owner");

                }
                return _headerColumn; 
            }
        }

        void GridViewColumnHeaderClickedHandler(object sender,
                                                RoutedEventArgs e)
        {
            GridViewColumnHeader headerClicked =
                  e.OriginalSource as GridViewColumnHeader;
            ListSortDirection direction;

            if (headerClicked != null)
            {
                if (headerClicked.Role != GridViewColumnHeaderRole.Padding)
                {
                    if (headerClicked != _lastHeaderClicked)
                    {
                        direction = ListSortDirection.Ascending;
                    }
                    else
                    {
                        if (_lastDirection == ListSortDirection.Ascending)
                        {
                            direction = ListSortDirection.Descending;
                        }
                        else
                        {
                            direction = ListSortDirection.Ascending;
                        }
                    }

                    
                    string header = headerClicked.Column.Header as string;
                    HeaderColumn.TryGetValue(header, out header);
                    Sort(header, direction);

                    ViewModel.Settings.FileSortHeader = header;
                    ViewModel.Settings.FileSortAscending = direction == ListSortDirection.Ascending;
                    BlobDataManager.Instance.SaveSettings(ViewModel.Settings);

                    _lastHeaderClicked = headerClicked;
                    _lastDirection = direction;
                }
            }

        }

        private void Sort(string sortBy, ListSortDirection direction)
        {
            ICollectionView dataView =
              CollectionViewSource.GetDefaultView(Files.ItemsSource);

            dataView.SortDescriptions.Clear();
            SortDescription sd = new SortDescription(sortBy, direction);
            dataView.SortDescriptions.Add(sd);
            dataView.Refresh();
        }
 
        void OnListViewItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.Handled)
                return;
            var textbox = VisualTreeHelperUtil.FindParent<TextBox>((DependencyObject)e.OriginalSource);
            if (textbox != null) return;

            ListViewItem item = VisualTreeHelperUtil.FindParent<ListViewItem>((DependencyObject)e.OriginalSource);
            if (item == null)
                return;

            if (item.Focusable && !item.IsFocused)
            {
                item.Focus();
            }
        }

        private FileItem EditingFile;

        private void ListViewItemRename_Click(object sender, RoutedEventArgs e)
        {
            if (Files.SelectedItem == null) return;

            var fileItem = (FileItem)Files.SelectedItem;
            fileItem.IsEditing = true;
            EditingFile = fileItem;
            fileItem.OldTitle = fileItem.Title;

        }


        private void FileTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                EndEditing();
            }
            if (e.Key == Key.Escape)
            {
                ResetEditing();
            }
        }

        private void EndEditing()
        {
            if (EditingFile != null)
            {
                if (EditingFile.OldTitle != EditingFile.Title)
                {
                    ViewModel.RenameFileCommand.Execute(EditingFile);
                }
                EditingFile.IsEditing = false;
                EditingFile = null;
            }
        }

        private void ResetEditing()
        {
            if (EditingFile != null)
            {
                EditingFile.IsEditing = false;
                EditingFile.Title = EditingFile.OldTitle;
                EditingFile = null;
            }
        }

        private void FileTextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            //var fo = Keyboard.FocusedElement as UIElement;
            //if (fo == Files && Files.SelectedItem == EditingFile) return;
            //ResetEditing();            
        }

        private void FileTextBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            var text = (TextBox)sender;
            if (text.Visibility == System.Windows.Visibility.Visible)
            {
                text.Focus();
                var index = -1;
                if (!string.IsNullOrEmpty(text.Text)) index = text.Text.LastIndexOf('.');
                if (index == -1) index = 0;
                text.Select(0, index);
            }

        }

        void Files_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (BlobDataManager.Instance.Offline) return;
            if (e.Key == Key.Enter && EditingFile == null)
            {                
                ViewModel.ExecuteFileCommand.Execute(e);
            }
        }

        #endregion


        #region treeview manual

        private void OnTreeItemExpanded(object sender, RoutedEventArgs e)
        {
            TreeViewItem item = e.OriginalSource as TreeViewItem;
            if (item != null)
            {
                var data = item.DataContext as DirectoryItem;
                if (data != null)
                {
                    ViewModel.LoadCommand.Execute(data);
                }
            }
        }  

         private void TreeViewItem_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (sender is TreeViewItem)
            {
                var item = sender as TreeViewItem;
                item.IsSelected = true;
                e.Handled = true;
            }
        }

        private DirectoryItem EditingFolder;

        private void TreeViewItemRename_Click(object sender, RoutedEventArgs e)
        {
            if (Folders.SelectedItem == null) return;

            var folderItem = (DirectoryItem)Folders.SelectedItem;
            folderItem.IsEditing = true;
            EditingFolder = folderItem;
            folderItem.OldTitle = folderItem.Title;

        }


        private void FolderTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                if (EditingFolder.OldTitle == EditingFolder.Title) return;
                ViewModel.RenameFolderCommand.Execute(EditingFolder);
                EditingFolder.IsEditing = false;
                EditingFolder = null;
            }
            if (e.Key == Key.Escape)
            {
                TreeViewResetEditing();
            }
        }

        private void TreeViewResetEditing()
        {
            if (EditingFolder != null)
            {
                EditingFolder.IsEditing = false;
                EditingFolder.Title = EditingFolder.OldTitle;
                EditingFolder = null;
            }
        }

        private void FolderTextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            TreeViewResetEditing();
        }

        private void FolderTextBox_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            var text = (TextBox)sender;
            if (text.Visibility == System.Windows.Visibility.Visible)
            {
                text.Focus();
                text.Select(0, 0);
            }

        }


        #endregion


        #region file menu

        private void OpenCommand_Click(object sender, RoutedEventArgs e)
        {

            ShowLoginDialog();
        }

        private void ShowLoginDialog()
        {
            var win = new LoginWindow();
            win.Main = ViewModel;
            win.ShowDialog();
        }

        private void ExitCommand_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void RecentFiles_Click(object sender, RoutedEventArgs e)
        {
            var item = (CachedFile)((MenuItem)sender).DataContext;
            var file = new FileItem() { Path = item.Path, LastModified = item.LastModified, };
            ViewModel.ExecuteFileCommand.Execute(file);
        }
        private void FavoriteList_Click(object sender, RoutedEventArgs e)
        {
            var item = (string)((MenuItem)sender).DataContext;
            ViewModel.LoadStartPathCommand.Execute(item);
        }

        private void FontSizes_Click(object sender, RoutedEventArgs e)
        {
            var item = (MenuItem)sender;
            ViewModel.SelectedFontSizes = (string)item.DataContext;
        }

        #endregion

        private void MenuItem_Checkin(object sender, RoutedEventArgs e)
        {
            var item = (CachedFile)((MenuItem)sender).DataContext;
            var file = new FileItem() { Path = item.Path, LastModified = item.LastModified, };
            ViewModel.CheckinCommand.Execute(file);
            FileMenu.IsSubmenuOpen = false;
        }

        private void MenuItem_CancelCheckout(object sender, RoutedEventArgs e)
        {
            var item = (CachedFile)((MenuItem)sender).DataContext;
            var file = new FileItem() { Path = item.Path, LastModified = item.LastModified, };
            ViewModel.CancelCheckoutCommand.Execute(file);
            FileMenu.IsSubmenuOpen = false;

        }

        private void MenuItem_Open(object sender, RoutedEventArgs e)
        {
            var item = (CachedFile)((MenuItem)sender).DataContext;
            var file = new FileItem() { Path = item.Path, LastModified = item.LastModified, };
            ViewModel.CurrentPath = ViewModel.GetParentDirectoryPath(file.Path);
            FileMenu.IsSubmenuOpen = false;
        }

        private void MenuItem_RemoveFavorite(object sender, RoutedEventArgs e)
        {
            var item = (string)((MenuItem)sender).DataContext;
            ViewModel.RemoveFavoriteCommand.Execute(item);
            
        }
    }

}
